using System;
using System.IO;
using System.Security.Cryptography;
using System.Text;
using Cysharp.Threading.Tasks;
using Neatly.Helper;
using Neatly.Module;
using Neatly.Native;
using UnityEngine;

namespace Neatly.Module
{
    public class FileModule : ModuleSingleton<FileModule>
    {
        public override async UniTask Init()
        {
#if UNITY_WEBGL
            return;
#endif
            CreateDirectory(NeatlyConfig.PATH_UPDATE_BUNDLE);
            CreateDirectory(NeatlyConfig.PATH_UPDATE_LUA);
            CreateDirectory(NeatlyConfig.PATH_UPDATE_I18N);
            NDebug.Log(NeatlyConfig.PATH_UPDATE_BUNDLE);
            NDebug.Log(NeatlyConfig.PATH_UPDATE_LUA);
            NDebug.Log(NeatlyConfig.PATH_UPDATE_I18N);
        }

        #region 文件操作
        public bool IsFileExist(string filePath)
        {
            return File.Exists(filePath);
        }

        public bool IsDirectoryExist(string directory)
        {
            return Directory.Exists(directory);
        }

        public bool CreateDirectory(string directory)
        {
            if (IsDirectoryExist(directory))
            {
                return true;
            }
            bool success = TryAction(() => { Directory.CreateDirectory(directory); },
                exception => { Debug.Log($"Create Directory {directory} Error! Exception = {exception}"); });
            return success;
        }

        public bool DeleteFile(string filePath)
        {
            if (!IsFileExist(filePath))
            {
                return true;
            }
            bool success = TryAction(() => { File.Delete(filePath); },
                exception => { Debug.Log($"Delete File {filePath} Error! Exception = {exception}"); });
            return success;
        }

        public bool DeleteDirectory(string directory)
        {
            if (!IsDirectoryExist(directory))
            {
                return true;
            }
            bool success = TryAction(() => { Directory.Delete(directory, true); },
                exception => { Debug.Log($"Delete Directory {directory} Error! Exception = {exception}"); });
            return success;
        }

        public void ClearDirectory(string directory)
        {
            DeleteDirectory(directory);
            CreateDirectory(directory);
        }

        public long GetFileLength(string filePath)
        {
            long fileLength = 0;
            if (!IsFileExist(filePath))
            {
                return fileLength;
            }
            TryAction(() =>
            {
                FileInfo fileInfo = new FileInfo(filePath);
                fileLength = fileInfo.Length;
            }, exception => { Debug.Log($"Get FileLength of {filePath} Error! Exception = {exception}"); });
            return fileLength;
        }

        public byte[] ReadFile(string filePath)
        {
            if (!IsFileExist(filePath))
            {
                return null;
            }
            byte[] array = null;
            TryAction(() => { array = File.ReadAllBytes(filePath); },
                exception => { Debug.Log($"Read File {filePath} Error! Exception = {exception}"); });
            return array;
        }

        public bool WriteFile(string filePath, byte[] data)
        {
            bool success = TryAction(() => { File.WriteAllBytes(filePath, data); },
                exception =>
                {
                    Debug.Log($"Write File {filePath} Error! Exception = {exception}");
                    DeleteFile(filePath);
                });
            return success;
        }

        public bool WriteFile(string filePath, byte[] data, int offset, int length)
        {
            FileStream fileStream = null;
            bool success = TryAction(() =>
                {
                    fileStream = new FileStream(filePath, FileMode.OpenOrCreate, FileAccess.Write, FileShare.ReadWrite);
                    fileStream.Write(data, offset, length);
                    fileStream.Close();
                },
                exception =>
                {
                    Debug.Log($"Write File {filePath} Error! Exception = {exception}");
                    DeleteFile(filePath);
                }, () => { fileStream?.Close(); });
            return success;
        }

        public byte[] ReadFileBytes(string path)
        {
#if !UNITY_EDITOR
        var byteArray = ReadFile($"{NeatlyConfig.PATH_UPDATE}/{path}");
        if (byteArray != null)
        {
            return byteArray;
        }
#if UNITY_ANDROID
        byteArray = AndroidHelper.ReadBytes(path);
#else
        byteArray = ReadFile($"{Application.streamingAssetsPath}/{path}");
#endif
        return byteArray;
#endif
            return ReadEditorFile(path);
        }

        public byte[] ReadEditorFile(string root, string fileName)
        {
            var path = $"assets/{root}/{fileName}";
            return ReadFile(path);
        }

        public byte[] ReadEditorFile(string path)
        {
            var byteArray = ReadFile($"assets/{path}");
            if (byteArray != null)
            {
                return byteArray;
            }
            return ReadFile($"{Application.streamingAssetsPath}/{path}");
        }

        public byte[] ReadUpdateLua(string fileName)
        {
            string path = $"{NeatlyConfig.PATH_UPDATE_LUA}/{fileName}";
            return ReadFile(path);
        }

        public byte[] ReadUpdateHashFile(int patch)
        {
            string path = $"{NeatlyConfig.PATH_UPDATE}/{NeatlyConfig.GetFileHashName(patch)}";
            return ReadFile(path);
        }

        public bool IsExitsUpdateFile(string folderName, string fileName)
        {
            string path = Path.Combine(NeatlyConfig.PATH_UPDATE, folderName, fileName);
            return IsFileExist(path);
        }

        public byte[] ReadBytes(string path)
        {
#if !UNITY_EDITOR && UNITY_ANDROID
        // 如果是读apk包里的资源,使用Android帮助库加载
        if (path.Contains(NeatlyConfig.PATH_PACKAGE_BUNDLE) || path.Contains(NeatlyConfig.PATH_PACKAGE_LUA))
        {
            string fileName = path.Replace(NeatlyConfig.PATH_PACKAGE + "/", "");
            return AndroidHelper.ReadBytes(fileName);
        }
#endif
            if (!IsFileExist(path))
            {
                Debug.Log("error:" + path);
            }
            return File.ReadAllBytes(path);
        }

        public byte[] ReadEditorLua(string fileName)
        {
            var path = $"assets/lua/{fileName}.lua";
            if (!IsFileExist(path))
            {
                path = $"assets/tolua/lua/{fileName}.lua";
            }
            return ReadFile(path);
        }

        public void CopyFile(string path, string outPath)
        {
#if !UNITY_EDITOR && UNITY_ANDROID
        string fileName = path.Replace(NeatlyConfig.PATH_PACKAGE + "/", "");
        AndroidHelper.CopyFile(fileName, outPath);
        return;
#endif
            File.Copy(path, outPath, true);
        }
        #endregion
        #region Util
        private static bool TryAction(Action doAction, Action<Exception> exceptionExitAction, Action failAction = null)
        {
            int count = 3;
            bool success = false;
            for (int i = 0; i < count; i++)
            {
                try
                {
                    doAction();
                    success = true;
                    break;
                }
                catch (Exception ex)
                {
                    failAction?.Invoke();
                    exceptionExitAction(ex);
                }
            }
            return success;
        }
        #endregion
    }
}